package org.wz.complex.work.controller.base;


import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;

import jakarta.validation.Valid;
import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.wz.complex.work.anno.*;
import org.wz.complex.work.bean.Idinfo;
import org.wz.complex.work.bean.Pagi;
import org.wz.complex.work.bean.Res;

import org.wz.complex.work.inters.CopyGroup;
import org.wz.complex.work.inters.InsertGroup;
import org.wz.complex.work.inters.UpdateGroup;
import org.wz.complex.work.kit.clz.ClzKit;
import org.wz.complex.work.kit.clz.FieldKit;
import org.wz.complex.work.kit.str.StrKit;
import org.wz.complex.work.service.base.BaseService;


import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;


@SuppressWarnings("all")
@Log4j2
public class BaseController<T, M> {

    protected Log log = LogFactory.getLog(getClass());

    @Value("${spring.profiles.active}")
    protected String springProfilesActive;

    private Class<T> getType() throws Exception {
        Type t = getClass().getGenericSuperclass();
        ParameterizedType p = (ParameterizedType) t ;
        Class<T> c = (Class<T>) p.getActualTypeArguments()[0];
        return c;
    }

    private Class<M> getTypeParent() throws Exception {
        Type t = getClass().getGenericSuperclass();
        ParameterizedType p = (ParameterizedType) t ;
        Class<M> c = (Class<M>) p.getActualTypeArguments()[1];
        return c;
    }

    protected BaseService<T> getService(String name)  {
       try {
           Field field = getClass().getDeclaredField(name);
           field.setAccessible(true);
           Object object = field.get(this);
           return (BaseService<T>) object;
       } catch (Exception e) {
           throw new RuntimeException(e);
       }
    }

    protected BaseService<T> getService()  {
        try {
            return getService("service");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected BaseService<T> getParentService() {
        try {
            return getService("parentService");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }



    @GetMapping("/page")
    public Res page(T t, Pagi<T> pagi) throws Exception {
        List<T> list = new ArrayList<>();
        QueryWrapper<T> qw = new QueryWrapper<>();

        setQw(qw, t, pagi);

        long count = getService().count(qw);
        pagi.setTotal(count);
        if (count > 0) {
            RemoveCols removeCols = t.getClass().getDeclaredAnnotation(RemoveCols.class);
            if (null != removeCols) {
                List<String> dbNames = ClzKit.getDbNames(t.getClass(), removeCols.value());
                qw.select(dbNames);
            }
            //
            IPage<T> ipage = new Page<>(pagi.getCpage(), pagi.getCsize());
            list = getService().page(ipage, qw).getRecords();
            if (null != list && list.size() > 0) {
                list.forEach(entity -> {
                    // composeEntity
                    Arrays.stream(entity.getClass().getDeclaredFields())
                            .filter(field -> field.getDeclaredAnnotation(ComposeEntity.class) != null)
                            .forEach(field -> {
                                String fieldName = field.getName();
                                Object idValue = FieldKit.getValueByObject(entity, fieldName + "Id");
                                if (null != idValue) {
                                    long id = Long.parseLong(String.valueOf(idValue));
                                    RemoveCols rem = field.getDeclaredAnnotation(RemoveCols.class);
                                    IService<T> is = getService(fieldName + "Service");
                                    Object objValue = null;
                                    if (null == rem) {
                                        objValue = is.getById(id);
                                    } else {
                                        List<String> dbNames = ClzKit.getDbNames(field.getType(), rem.value());
                                        QueryWrapper oqw = new QueryWrapper<>();
                                        oqw.eq("id", id);
                                        oqw.select(dbNames);
                                        objValue = is.getOne(oqw);
                                    }
                                    FieldKit.setValue(entity, field, objValue);
                                }
                            });
                    // after
                    after(entity);
                    afterSub(entity);
                });
            }
        }
        pagi.setList(list);
        return Res.ok(pagi);
    }

    protected void setQw(QueryWrapper<T> qw, T t, Pagi<T> pagi) {
        String order = pagi.getOrder();
        if ("desc".equals(order)) {
            qw.orderByDesc(pagi.getSort());
        } else {
            qw.orderByAsc(pagi.getSort());
        }
        // deploy
        Deploy deploy = t.getClass().getDeclaredAnnotation(Deploy.class);
        if (null != deploy) {
            qw.eq("deploy", springProfilesActive);
        }
        // more
        FieldKit.foreachAnnotation(QueryConf.class, t, (field, anno) -> {

            Object value = FieldKit.getValue(field, t);
            if (null != value) {
                String type = field.getType().getSimpleName().toLowerCase();
                QueryConf queryConf = (QueryConf) anno;
                String conf = queryConf.value();
                String under = StrUtil.toUnderlineCase(field.getName()).toUpperCase();
                switch (type) {
                    case "string":
                        if (StrKit.notNull((String) value)) {
                            if (conf.equals("=")) {
                                qw.eq(under, value);
                            } else if (conf.equals("in")) {
                                String[] split = ((String) value).split(",");
                                List<String> inlist = Stream.of(split).map(String::trim).toList();
                                qw.in(under, inlist);
                            } else {
                                qw.like(under, value);
                            }
                        }
                        break;
                    default:
                        qw.eq(under, value);
                }
            }
        });
    }



    protected void afterSub(T t) {

    }

    protected void after(T t) {

    }



    // insert
    @Transactional
    @PostMapping("/insert")
    public Res insert(
          @Validated(InsertGroup.class) @RequestBody T t) throws Exception {

        boolean flag = validInsert(t);
        if (!flag) {
            return Res.fail("[/insert] 参数校验失败！");
        }
        // beforeInsert
        beforeInsert(t);

        // save
        getService().save(t);
        return Res.ok(1);
    }

    protected boolean validInsert(T t) {
        return true;
    }

   //  beforeInsert
    protected void beforeInsert(T t) {

    }

    // insert
    @Transactional
    @PutMapping("/update_by_id")
    public Res updateById(
            @Validated(UpdateGroup.class)  @RequestBody T t) throws Exception {
        boolean flag = validUpdateById(t);
        if (!flag) {
            return Res.fail("【/update_by_id】参数校验失败！");
        }

        beforeUpdateById(t);

        //
        getService().updateById(t);
        return Res.ok(1);
    }

    protected void beforeUpdateById(T t) {

    }

    protected boolean validUpdateById(T t) {
        return true;
    }


    @Transactional
    @DeleteMapping("/remove_ids")
    public Res removeIds(Idinfo idinfo) throws Exception {
        List<Long> idList = new ArrayList<>();
        try {
            idList = Stream.of(idinfo.getIds().split(","))
                    .map(String::trim)
                    .map(Long::parseLong)
                    .toList();
        } catch (Exception e) {
            e.printStackTrace();
            return Res.fail("【/remove_ids】参数校验失败！");
        }
        boolean flag = validRemoveIds(idList, idinfo);
        if (!flag) {
            return Res.fail("【/remove_ids】参数校验失败！");
        }
        // TODO 判断是否有子表


        getService().removeByIds(idList);
        return Res.ok(1);
    }


    protected boolean validRemoveIds(List<Long> idList, Idinfo idinfo) {
        if (idList != null && idList.size() > 0) {
            return true;
        }
        return false;
    }




    @Transactional
    @PostMapping("/copy")
    public Res copy(@Validated(CopyGroup.class) @RequestBody Idinfo idinfo) throws Exception {
        Long id = idinfo.getId();
        Long initId = idinfo.getId();
        T t = getService().getById(id);
        if (null != t) {
            // 清空id
            FieldKit.setValueByProperty(t, "id", null);

            // 设置唯一性约束的字段
            FieldKit.foreachAnnotation(UniqueCol.class, t, (field, anno) -> {
                Object value = FieldKit.getValue(field, t);
                if (null != value) {
                    String type = field.getType().getSimpleName().toLowerCase();
                    Object last = null;
                    switch (type) {
                        case "string":
                            last = String.valueOf(value) + "__" + IdUtil.getSnowflakeNextIdStr() ;
                            break;
                        case "integer":
                            last = Integer.parseInt(String.valueOf(IdUtil.getSnowflakeNextId()).substring(0, 9));
                            break;
                        case "long":
                            last = IdUtil.getSnowflakeNextId();
                            break;
                    }
                    try {
                        Method md = t.getClass().getDeclaredMethod("set" + StrUtil.upperFirst(field.getName()) , field.getType());
                        md.setAccessible(true);
                        md.invoke(t, last);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            });

            System.out.println("haha");

            // 保存
            getService().save(t);

//            Object type = FieldKit.getValueByObject(t, "type");
//
//            if ("table".equals(type)) {
//                // 复制子表
//                copySub(t, initId, Long.parseLong(String.valueOf(FieldKit.getValueByObject(t, "id"))));
//            } else {
//                throw new RuntimeException("只有表格类型【】才能复制子表！");
//            }

        }
        return Res.ok("复制成功！ 新的id: " + FieldKit.getValueByObject(t, "id"));
    }

    private void copySub(T t, Long initId, Long newId) {
        Copy copy = t.getClass().getDeclaredAnnotation(Copy.class);
        if (null != copy) {
            String simpleName = copy.service().getSimpleName();
            String serviceName = StrUtil.lowerFirst(simpleName);
            QueryWrapper qw = new QueryWrapper();
            qw.eq(copy.relatePid(), initId);
            qw.last("limit 1");
            Object one = getService(serviceName).getOne(qw);
            if (null != one) {
                FieldKit.setValueByProperty(one, copy.relatePid(), newId);
                getService(serviceName).copy2(one, copy.entity());
            }
        }
    }


}
